package producer.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.ByteString;
import common.parse.StringParser;
import common.proto.JSONUtil;
import common.proto.RPCProtoParamOuterClass;
import common.proto.RPCProtoReqOuterClass;
import common.proto.RPCProtoRespOuterClass;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import producer.config.ServiceConfig;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.List;

/**
 * @ClassName RpcHandler
 * @Description
 * @Author Jingxiao.Ye
 * @Date 2022/1/2 下午11:43
 * @Version V1.0
 * @Since JDK 11
 */
public class RpcHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception {
        try {
            ByteBuf content = fullHttpRequest.content();
            byte[] bytes = ByteBufUtil.getBytes(content);
            RPCProtoReqOuterClass.RPCProtoReq rpcProtoReq = RPCProtoReqOuterClass.RPCProtoReq.parseFrom(bytes);
            String interfaceClassName = rpcProtoReq.getClassName();
            String methodName = rpcProtoReq.getMethodName();
            ByteString params = rpcProtoReq.getParams();
            Class[] paramClasses = null;
            Object[] paramObjs = null;
            if(!params.isEmpty()){
                List<ByteBuffer> byteBuffers = params.asReadOnlyByteBufferList();
                paramClasses = new Class[byteBuffers.size()];
                paramObjs = new Object[byteBuffers.size()];
                int cnt = 0;
                for (ByteBuffer byteBuffer : byteBuffers) {
                    RPCProtoParamOuterClass.RPCProtoParam rpcProtoParam = RPCProtoParamOuterClass.RPCProtoParam.parseFrom(byteBuffer);
                    String paramClassName = rpcProtoParam.getClassName();
                    //通过名字解析content:如果是str
                    ByteString content1 = rpcProtoParam.getContent();
                    Class<?> aClass = Class.forName(paramClassName);
                    paramClasses[cnt] = aClass;
                    String parse = StringParser.parse(content1.toByteArray());
                    paramObjs[cnt] = JSONUtil.objectMapper.readValue(parse, Object.class);
                    cnt++;
                }
            }

            //反射查找类producer.service
            Object o = ServiceConfig.serviceMap.get(interfaceClassName);
            Class<?> aClass = o.getClass();

            Object invoke = null;
            if(paramClasses != null){
                Method declaredMethod = aClass.getDeclaredMethod(methodName, paramClasses);
                invoke = declaredMethod.invoke(o, paramObjs);
            }else {
                Method declaredMethod = aClass.getDeclaredMethod(methodName);
                invoke = declaredMethod.invoke(o);
            }

            RPCProtoRespOuterClass.RPCProtoResp.Builder builder = RPCProtoRespOuterClass.RPCProtoResp.newBuilder();
            if(invoke != null){
                String s = JSONUtil.objectMapper.writeValueAsString(invoke);
                builder.setResult(ByteString.copyFrom(s.getBytes()));
            }
            RPCProtoRespOuterClass.RPCProtoResp rpcProtoResp = builder.build();
            byte[] respBytes = rpcProtoResp.toByteArray();
            ByteBuf byteBuf = Unpooled.wrappedBuffer(respBytes);
            DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK, byteBuf);
            defaultFullHttpResponse.headers().set("Content-Type","text/plain").set("Content-Length",respBytes.length);

            channelHandlerContext.writeAndFlush(defaultFullHttpResponse).addListener(ChannelFutureListener.CLOSE);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}
